package fybug.nulll.pdstream.Source;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;

import fybug.nulll.pdfunctionlibrary.Annotations.MaybeStop;
import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.DataContainer;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.Source.Interface.CheckData;

import static fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys.checkNullArray;
import static fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys.conversionArrayType;
import static fybug.nulll.pdstream.StreamOperator.getReader;
import static fybug.nulll.pdstream.StreamOperator.getWriter;
import static fybug.nulll.pdstream.StreamOperator.toBufferStream;
/**
 * <h2>流操作源.</h2>
 * <pre>
 * 作为所有流操作器的 <b>操作源(芯片)</b>
 * 需指定数据类型和绑定检查接口
 * 请使用工场方法 {@link Bytes} 或 {@link Strings} 生成
 * </pre>
 *
 * @param <T> 被操作的流
 * @param <E> 输出数据的类型
 *
 * @author fybug
 * @version 0.3.3
 * @see DataContainer
 * @see CheckData
 * @see Closeable
 * @see MaybeStop
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public final
class StreamSource<T extends Closeable, E> extends DataContainer<E> {
    /**
     * 读写流<br/>
     * <b>{@code T[]}</b>
     */
    @Nullable private Object[] closeable = new Object[1];
    // 数组类型
    @NotNull private final Class<T[]> tClass;
    // 同步锁
    @NotNull private Object closebleLock = new Object();
    // 数据放入接口
    @Nullable private CheckData<E> check;

    /**
     * <p>构造一个操作源,并绑定操作流.</p>
     * <pre>
     * 需要传入数组类型
     * 如果使用 <b>输入流</b> 则将数据类型改为 {@link Object}
     * </pre>
     *
     * @param tClass 数组类型
     * @param st     操作流
     */
    public
    StreamSource(@NotNull final Class<T[]> tClass, @Nullable final T st)
    {
        this.tClass = tClass;
        binStream(st);
    }

    @Override
    public final
    int hashCode()
    { return (((getClass().hashCode() + closebleLock.hashCode()) << 5) + super.hashCode()) << 5; }

    @Override
    protected final
    boolean consistent(@NotNull final Object o) {
        @NotNull final StreamSource<T, E> obj = (StreamSource) o;
        boolean bo; // 检查字段
        /* 检查绑定数据 */
        @NotNull final E t; // 当前绑定数据
        try {
            t = getValue();
            try {
                bo = Check.equalsFidle(t, obj.getValue());
            } catch ( Stop e ) {
                return false;
            }
        } catch ( Stop e ) {
            try {
                obj.getValue();
                return false;
            } catch ( Stop ee ) {
                bo = true;
            }
        }
        /* 检查流 */
        bo &= Check.equalsArrayTable(closeable, obj.closeable);
        // 检查数组类型
        bo &= tClass == obj.tClass;
        return bo;
    }

    /*
     * MainStream
     */

    /**
     * <p>绑定操作流.</p>
     * <p>拷贝一个新的数组并去除空项和检查长度</p>
     *
     * @param o 要绑定到被绑定的源中的流
     *
     * @return this
     */
    @NotNull
    public final
    StreamSource<T, E> binStream(@Nullable T... o) {
        @Nullable final Object[] closeable;
        synchronized ( closebleLock ){
            closeable = this.closeable;
            if (closeable != null) {
                // 检查长度和去除空格
                o = checkNullArray(Arrarys.trim(o));
                // 复制一个新的
                if (o != null)
                    o = Arrays.copyOf(o, o.length, tClass);
                closeable[0] = o;
            }
        }
        return this;
    }

    /**
     * <p>获取绑定的操作流.</p>
     *
     * @return 绑定的流
     */
    @NotNull
    @MaybeStop
    public final
    T[] stream()
    { return (T[]) Check.checkArrayTable(this.closeable); }

    /*
     * Check
     */

    /**
     * <p>绑定检查接口.</p>
     * <p>通过该接口进行数据绑定时和输出获取时的校验和检查</p>
     *
     * @param check 检查接口
     *
     * @return this
     *
     * @see CheckData
     */
    @NotNull
    public synchronized final
    StreamSource<T, E> binCheck(@Nullable final CheckData<E> check) {
        this.check = check;
        return this;
    }

    /*
     * OutData
     */

    /**
     * <p>绑定要输出的对象.</p>
     *
     * @param e 要绑定的数据
     *
     * @return this
     */
    @NotNull
    public final
    StreamSource<T, E> binOutPut(@Nullable E e) {
        if (check != null)
            e = check.put(e);
        setValue(e);
        return this;
    }

    /**
     * <p>获取绑定的输出对象.</p>
     *
     * @return 绑定的数据
     */
    @NotNull
    @MaybeStop
    public final
    E getOutPut() {
        @NotNull Object e = getValue();
        if (check != null)
            e = check.get(e);
        return (E) e;
    }

    /*
     * 释放资源
     */

    /**
     * <p>清空该类中的引用.</p>
     * <pre>
     * 仅清空该类,但不关闭其中的可关闭对象
     * 清空后该对象仍可复用
     * </pre>
     */
    @Override
    public final
    void clean() {
        binStream();
        super.clean();
    }

    /**
     * <p>释放该类的资源.</p>
     * <pre>
     * 清空该类并关闭该类中的所有资源
     * 释放后仍可复用
     * </pre>
     */
    @Override
    public final
    void free() {
        try {
            @NotNull Closeable[] ts = stream();
            synchronized ( ts ){
                // 关闭主流
                for ( int i = 0; i < ts.length; i++ )
                    try {
                        ts[i].close();
                    } catch ( Throwable ignored ) {
                    }
            }
        } catch ( Stop ignored ) {
        }
        super.free();
    }

    /**
     * <p>彻底关闭该类.</p>
     * <pre>
     * 清空该类并关闭该类相关的所有资源
     * 关闭后不可复用
     * </pre>
     */
    @Override
    public final
    void close() {
        super.close();
        // 不可复用
        synchronized ( closebleLock ){
            closeable = null;
        }
    }

    /**
     * <h2>字节源构造工场.</h2>
     * <pre>
     * 用于构造字节流操作源,并包装为缓冲流
     *
     * 包含:
     * {@link #reader(InputStream...)} 构造读取源
     * {@link #writer(OutputStream, byte[])} 构造写入源
     * {@link #copy(OutputStream, InputStream)} 构造复制源
     * </pre>
     *
     * @author fybug
     * @version 0.0.6
     * @see StreamSource
     * @since JDK 1.8
     */
    @SuppressWarnings( "All" )
    public static final
    class Bytes {
        // 输出检查接口
        @NotNull private static final CheckData<byte[]> out = new CheckData<byte[]>() {
            @Override
            public
            byte[] put(@Nullable byte... data) {
                if (data == null)
                    data = new byte[0];
                // 检查长度
                // 拷贝
                return checkNullArray(Arrays.copyOf(data, data.length));
            }

            @Override
            public
            byte[] get(Object data) { return Check.checkTyple(data, byte[].class); }
        };
        // 复制检查接口
        @NotNull private static final CheckData<InputStream[]> copy =
                new CheckData<InputStream[]>() {
                    @Override
                    public
                    InputStream[] put(@Nullable InputStream... data) {
                        if (data == null)
                            data = new InputStream[0];
                        // 检查长度和去除空值
                        data = Arrarys.checkNullArray(Arrarys.trim(data));
                        // 拷贝
                        return (data == null ? null : Arrays.copyOf(data, data.length));
                    }

                    @Override
                    public
                    InputStream[] get(Object data)
                    { return Check.checkTyple(data, InputStream[].class); }
                };

        /** {@Hide} */
        @Deprecated
        private
        Bytes() {}

        /*
         * Read
         */

        /**
         * <p>构造一个字节流读取源.</p>
         * <p>可绑定多个读取流</p>
         */
        @NotNull
        public static
        StreamSource<InputStream, Object> reader(@Nullable final InputStream... reader) {
            if (reader.length == 1)
                return new StreamSource<>(InputStream[].class, toBufferStream(reader[0], 8192));
            return new StreamSource<>(InputStream[].class, null).binStream(
                    toBufferStream(8192, reader));
        }

        /** <p>构造一个文件读取源.</p> */
        @NotNull
        public static
        StreamSource<InputStream, Object> reader(@Nullable final File... files)
        throws FileNotFoundException
        {
            if (files.length == 1)
                return new StreamSource<>(InputStream[].class,
                                          toBufferStream(new FileInputStream(files[0]), 8192));
            return new StreamSource<>(InputStream[].class, null).binStream(
                    toBufferStream(8192, conversionArrayType(files, f -> {
                        try {
                            if (((File) f).isFile()) {
                                return new FileInputStream(f);
                            } else
                                return null;
                        } catch ( FileNotFoundException e ) {
                            return null;
                        }
                    }, InputStream.class)));
        }

        /*
         * Write
         */

        /**
         * <p>构造一个字节流写入源.</p>
         * <p>需包含要写入的字节数组</p>
         */
        @NotNull
        public static
        StreamSource<OutputStream, byte[]> writer(@Nullable final OutputStream writer,
                @Nullable final byte[] c)
        {
            return new StreamSource<OutputStream, byte[]>(OutputStream[].class,
                                                          toBufferStream(writer, 8192)).binCheck(
                    out).binOutPut(c);
        }

        /**
         * <p>构造一个字节写入源.</p>
         * <pre>
         * 可绑定多个写入流
         * 需包含要写入的字节数组
         * </pre>
         */
        @NotNull
        public static
        StreamSource<OutputStream, byte[]> writer(@Nullable final OutputStream[] writer,
                @Nullable final byte[] c)
        {
            @NotNull StreamSource<OutputStream, byte[]> e;
            if (writer.length == 1)
                e = new StreamSource<>(OutputStream[].class, toBufferStream(writer[0], 8192));
            else
                e = new StreamSource<OutputStream, byte[]>(OutputStream[].class, null).binStream(
                        toBufferStream(8192, writer));
            return e.binCheck(out).binOutPut(c);
        }

        /**
         * <p>构造一个文件写入源.</p>
         * <p>需包含要写入的字节数组</p>
         */
        @NotNull
        public static
        StreamSource<OutputStream, byte[]> writer(@Nullable final File writer,
                @Nullable final byte[] c) throws FileNotFoundException
        {
            return new StreamSource<OutputStream, byte[]>(OutputStream[].class, toBufferStream(
                    new FileOutputStream(writer), 8192)).binCheck(out).binOutPut(c);
        }

        /**
         * <p>构造一个文件写入源.</p>
         * <p>需包含要写入的字节数组</p>
         */
        @NotNull
        public static
        StreamSource<OutputStream, byte[]> writer(@Nullable final File[] writer,
                @Nullable final byte[] c) throws FileNotFoundException
        {
            @NotNull StreamSource<OutputStream, byte[]> e;
            if (writer.length == 1)
                e = new StreamSource<>(OutputStream[].class,
                                       toBufferStream(new FileOutputStream(writer[0]), 8192));
            else
                e = new StreamSource<OutputStream, byte[]>(OutputStream[].class, null).binStream(
                        toBufferStream(8192, conversionArrayType(writer, f -> {
                            try {
                                if (f.isFile()) {
                                    return new FileOutputStream(f);
                                } else
                                    return null;
                            } catch ( FileNotFoundException e1 ) {
                                return null;
                            }
                        }, OutputStream.class)));
            return e.binCheck(out).binOutPut(c);
        }

        /*
         * Copy
         */

        /**
         * <p>构造一个字节流复制器.</p>
         * <p>需包含要复制的流</p>
         */
        @NotNull
        public static
        StreamSource<OutputStream, InputStream[]> copy(@Nullable final OutputStream w,
                @Nullable final InputStream r)
        {
            return new StreamSource<OutputStream, InputStream[]>(OutputStream[].class,
                                                                 toBufferStream(w, 8192)).binCheck(
                    copy).binOutPut(new InputStream[]{toBufferStream(r, 8192)});
        }

        /**
         * <p>构造一个批量字节流复制器.</p>
         * <pre>
         * 可绑定多个写入流进行批量复制
         * 需传入要复制的流,可以传入多个
         * </pre>
         */
        @NotNull
        public static
        StreamSource<OutputStream, InputStream[]> copy(@Nullable final OutputStream[] w,
                @Nullable final InputStream[] r)
        {
            if (w.length == 1 && r.length == 1)
                return copy(w[0], r[0]);
            return new StreamSource<OutputStream, InputStream[]>(OutputStream[].class,
                                                                 null).binStream(
                    toBufferStream(8192, w)).binCheck(copy).binOutPut(toBufferStream(8192, r));
        }
    }

    /**
     * <h2>字符源构造工场.</h2>
     * <pre>
     * 用于构造字符流操作源,并包装为缓冲流
     *
     * 包含:
     * {@link #reader(Reader...)} 构造读取源
     * {@link #writer(Writer, CharSequence)} 构造写入源
     * {@link #copy(Writer, Reader)} 构造复制源
     * </pre>
     *
     * @author fybug
     * @version 0.1.4
     * @see TextSource
     * @since JDK 1.8
     */
    @SuppressWarnings( "all" )
    public static final
    class Strings {
        // 输出检查接口
        @NotNull final private static CheckData<CharSequence> writer =
                new CheckData<CharSequence>() {
                    @Override
                    public
                    CharSequence put(@Nullable final CharSequence data) {
                        return fybug.nulll.pdfunctionlibrary.Util.Processing.Strings.getString(
                                data);
                    }

                    @Override
                    public
                    CharSequence get(@Nullable final Object data)
                    { return Check.checkTyple(data, String.class); }
                };
        // 复制检查接口
        @NotNull final private static CheckData<Reader[]> copy = new CheckData<Reader[]>() {
            @Override
            public
            Reader[] put(Reader[] data) {
                // 检查长度和去除空值
                data = Arrarys.checkNullArray(Arrarys.trim(data));
                // 拷贝
                return (data == null ? null : Arrays.copyOf(data, data.length));
            }

            @Override
            public
            Reader[] get(Object data) { return Check.checkTyple(data, Reader[].class); }
        };

        /** {@Hide} */
        @Deprecated
        private
        Strings() {}

        /*
         * Read
         */

        /**
         * <p>构造一个字符流读取源.</p>
         * <p>可绑定多个读取流</p>
         */
        @NotNull
        public static
        StreamSource<Reader, Object> reader(@Nullable final Reader... reader) {
            if (reader.length == 1)
                return new StreamSource<>(Reader[].class, toBufferStream(reader[0], 8192));
            return new StreamSource<>(Reader[].class, null).binStream(toBufferStream(8192, reader));
        }

        /**
         * <p>使用字节流构造一个字符流读取源.</p>
         * <p>可绑定多个读取流,需传入使用的 <b>字符编码</b></p>
         */
        @NotNull
        public static
        StreamSource<Reader, Object> reader(@NotNull final String charname,
                @Nullable final InputStream... reader)
        { return reader(getReader(charname, reader)); }

        /**
         * <p>构造一个文本文件读取源.</p>
         * <p>需传入使用的 <b>字符编码</b></p>
         */
        @NotNull
        public static
        StreamSource<Reader, Object> reader(@NotNull final String charname,
                @Nullable final File... reader)
        {
            return reader(charname, conversionArrayType(reader, f -> {
                try {
                    return new FileInputStream(f);
                } catch ( FileNotFoundException e ) {
                    return null;
                }
            }, InputStream.class));
        }

        /*
         * Write
         */

        /**
         * <p>构造一个字符写入源.</p>
         * <p>需包含要写入的字符</p>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@Nullable final Writer writer,
                @Nullable final CharSequence c)
        {
            return new StreamSource<Writer, CharSequence>(Writer[].class,
                                                          toBufferStream(writer, 8192)).binCheck(
                    Strings.writer).binOutPut(c);
        }

        /**
         * <p>使用字节流构造一个字符写入源.</p>
         * <p>需包含要写入的字符,需传入使用的 <b>字符编码</b></p>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@NotNull final String charname,
                @Nullable final OutputStream writer, @Nullable final CharSequence c)
        {
            return new StreamSource<Writer, CharSequence>(Writer[].class, toBufferStream(
                    getWriter(charname, writer), 8192)).binCheck(Strings.writer).binOutPut(c);
        }

        /**
         * <p>构造一个文本文件写入源.</p>
         * <p>需包含要写入的字符,需传入使用的 <b>字符编码</b></p>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@NotNull final String charname,
                @Nullable final File writer, @Nullable final CharSequence c)
        throws FileNotFoundException
        {
            return new StreamSource<Writer, CharSequence>(Writer[].class, toBufferStream(
                    getWriter(charname, new FileOutputStream(writer)), 8192)).binCheck(
                    Strings.writer).binOutPut(c);
        }

        /**
         * <p>构造一个字符写入源.</p>
         * <pre>
         * 可绑定多个写入流
         * 需包含要写入的字符
         * </pre>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@Nullable final Writer[] writer,
                @Nullable final CharSequence c)
        {
            @NotNull StreamSource<Writer, CharSequence> e;
            if (writer.length == 1)
                e = new StreamSource<>(Writer[].class, toBufferStream(writer[0], 8192));
            else
                e = new StreamSource<>(Writer[].class, null);
            return e.binStream(toBufferStream(8192, writer)).binCheck(Strings.writer).binOutPut(c);
        }

        /**
         * <p>使用字节流构造一个字符写入源.</p>
         * <pre>
         * 可绑定多个写入流
         * 需包含要写入的字符
         * 需传入使用的 <b>字符编码</b>
         * </pre>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@NotNull final String charname,
                @Nullable final OutputStream[] writer, @Nullable final CharSequence c)
        {
            if (writer.length == 1)
                return writer(charname, writer[0], c);
            return writer(getWriter(charname, writer), c);
        }

        /**
         * <p>一个文本文件写入源.</p>
         * <pre>
         * 需包含要写入的字符
         * 需传入使用的 <b>字符编码</b>
         * </pre>
         */
        @NotNull
        public static
        StreamSource<Writer, CharSequence> writer(@NotNull final String charname,
                @Nullable final File[] writer, @Nullable final CharSequence c)
        throws FileNotFoundException
        {
            if (writer.length == 1)
                return writer(charname, new FileOutputStream(writer[0]), c);
            return writer(getWriter(charname, conversionArrayType(writer, file -> {
                try {
                    return new FileOutputStream(file);
                } catch ( FileNotFoundException e ) {
                    return null;
                }
            }, OutputStream.class)), c);
        }

        /*
         * Copy
         */

        /**
         * <p>构造一个文本复制器.</p>
         * <p>需包含要复制的流</p>
         */
        @NotNull
        public static
        StreamSource<Writer, Reader[]> copy(@Nullable final Writer w, @Nullable final Reader r)
        {
            return new StreamSource<Writer, Reader[]>(Writer[].class, w).binCheck(copy).binOutPut(
                    new Reader[]{toBufferStream(r, 8192)});
        }

        /**
         * <p>使用字节流构造一个文本复制器.</p>
         * <p>需包含要复制的流,需传入使用的 <b>字符编码</b></p>
         */
        @NotNull
        public static
        StreamSource<Writer, Reader[]> copy(@NotNull final String charname,
                @Nullable final OutputStream w, @Nullable final InputStream r)
        { return copy(getWriter(charname, w), getReader(charname, r)[0]); }

        /**
         * <p>构造一个批量文本复制器.</p>
         * <pre>
         * 可绑定多个写入流进行批量复制
         * 需传入要复制的流,可以传入多个
         * </pre>
         */
        @NotNull
        public static
        StreamSource<Writer, Reader[]> copy(@Nullable final Writer[] w, @Nullable final Reader[] r)
        {
            if (w.length == 1 && r.length == 1)
                return copy(w[0], r[0]);
            return new StreamSource<Writer, Reader[]>(Writer[].class, null).binStream(
                    toBufferStream(8192, w)).binCheck(copy).binOutPut(toBufferStream(8192, r));
        }

        /**
         * <p>使用字节流构造一个批量文本复制器.</p>
         * <pre>
         * 可绑定多个写入流进行批量复制
         * 需传入要复制的流,可以传入多个
         * 需传入使用的 <b>字符编码</b>
         * </pre>
         */
        @NotNull
        public static
        StreamSource<Writer, Reader[]> copy(@NotNull final String charname,
                @Nullable final OutputStream[] w, @Nullable final InputStream[] r)
        {
            if (w.length == 1 && r.length == 1)
                return copy(charname, w[0], r[0]);
            return copy(getWriter(charname, w), getReader(charname, r));
        }
    }
}
